open Alcotest
open Mcdb__Mcdb2

let set_up () =
  Unix.chdir "../../../mcdb/"

let tc_strstrmap () =
  let module StrTuple = struct
    type t = string * string
    let compare (x0,y0) (x1,y1) =
      match String.compare x0 x1 with
        0 -> String.compare y0 y1
      | c -> c
  end in
  let module StrMap = Map.Make(StrTuple) in
  let m = StrMap.(empty |> add ("a","A") "aa") in
  StrMap.find_opt ("a","A") m
  |> Option.value ~default:"b"
  |> check string __LOC__ "aa"

let tc_read4b () =
  let open Bigarray in
  let fd = Unix.openfile "testdata/mini.cdb" [ Unix.O_RDONLY ] 0 in
  let mf = Unix.map_file fd int8_unsigned c_layout false [| -1 |] in
  let arr = array1_of_genarray mf in
  let _len = Array1.dim arr in
  0 |> read4b arr |> check Alcotest.int __LOC__ 2080;
  256 * 8 + 0 |> read4b arr |> check Alcotest.int __LOC__ 1;
  256 * 8 + 4 |> read4b arr |> check Alcotest.int __LOC__ 2;
  fd |> Unix.close

let tc_blit () =
  let open Bigarray in
  let fd = Unix.openfile "testdata/mini.cdb" [ Unix.O_RDONLY ] 0 in
  let mf = Unix.map_file fd int8_unsigned c_layout false [| -1 |] in
  let arr = array1_of_genarray mf in
  256 * 8 + 0 |> read4b arr |> check Alcotest.int __LOC__ 1;
  256 * 8 + 4 |> read4b arr |> check Alcotest.int __LOC__ 2;
  let b0 = blit arr (256 * 8 + 2*4) (1) in
  let b1 = blit arr (256 * 8 + 2*4 + 1) (2) in
  fd |> Unix.close;
  b0 |> String.of_bytes |> check string __LOC__ "a";
  b1 |> String.of_bytes |> check string __LOC__ "Ä"

let tc_record () =
  let open Bigarray in
  let fd = Unix.openfile "testdata/mini.cdb" [ Unix.O_RDONLY ] 0 in
  let mf = Unix.map_file fd int8_unsigned c_layout false [| -1 |] in
  let arr = array1_of_genarray mf in
  let (k,v) = 256*8 |> read_record arr in
  fd |> Unix.close;
  k |> String.of_bytes |> check string __LOC__ "a";
  v |> String.of_bytes |> check string __LOC__ "Ä"

let tc_fold_left () =
  (match "testdata/mini.cdb"
         |> fold_left
           (fun init (_,(k,v)) -> (k |> Bytes.to_string, v |> Bytes.to_string) :: init)
           []
         |> List.rev with
  | [("a","Ä");
     ("b","B");
     ("s","ß");] -> ()
  | _ -> Alcotest.fail __LOC__);
  match "testdata/mini.cdb"
        |> fold_left
          (fun init (_,(k,v)) -> (k |> Bytes.to_string, v |> Bytes.to_string) :: init)
          []
        |> List.rev with
  | [("a","Ä");
     ("b","B");
     ("s","ß");] -> ()
  | _ -> Alcotest.fail __LOC__

let tc_fold_left_key () =
  let k = "b" |> Bytes.of_string in
  (match "testdata/mini.cdb"
         |> fold_left_key k
           (fun init v -> (v |> Bytes.to_string) :: init)
           []
         |> List.rev with
  | ["B"] -> ()
  | _ -> Alcotest.fail __LOC__);
  let k = "b" |> Bytes.of_string in
  "testdata/mini-dup.cdb"
  |> fold_left_key k
    (fun init v -> (v |> Bytes.to_string) :: init)
    []
  |> List.rev
  |> List.length
  |> check int __LOC__ 20

let tc_find_first_key () =
  let k = "b" |> Bytes.of_string in
  (match "testdata/mini.cdb"
         |> find_first_key k
   with
   | Some b -> b |> Bytes.to_string |> check string __LOC__ "B"
   | _ -> Alcotest.fail __LOC__)

let tc_array () =
  (* an array with 256 entries *)
  (* each entry: hash + position *)
  ()

let tc_scan_data () =
  (* let l = Mcdb__Mcdb2.scan_data "testdata/mini-dup.cdb" in *)
  (* an array with 256 entries *)
  (* each entry: hash + position *)
  (* l |> List.length |> check int __LOC__ 0 *)
  let i = IntMap.(empty |> add 1 [1000]) in
  i |> IntMap.cardinal |> check int __LOC__ 1;
  (match i |> IntMap.find_opt 1 with
   | Some [v] -> v |> check int __LOC__ 1000
   | _ -> fail __LOC__ );
  let i = i |> IntMap.add 1 (match i |> IntMap.find_opt 1 with
      | Some l -> 1001 :: l
      | None -> []) in
  i |> IntMap.cardinal |> check int __LOC__ 1;
  (match i |> IntMap.find_opt 1 with
   | Some [a; b] ->
     a |> check int __LOC__ 1001;
     b |> check int __LOC__ 1000
   | _ -> fail __LOC__ );
  let a = Array.make 256 IntMap.empty in
  (1234,12) |> kv_add' a;
  (* (1234,12) |> kv_add' a; *)
  (1234,13) |> kv_add' a;
  (1234,14) |> kv_add' a;
  (1235,15) |> kv_add' a;
  let hm = a.(1235 mod 256) in
  hm |> IntMap.cardinal |> check int __LOC__ 1;
  (match hm |> IntMap.find_opt 1235 with
   | Some s -> s |> IntSet.find_opt 15 |> Option.value ~default:3 |> check int __LOC__ 15
   | _ -> fail __LOC__);
  let hm = a.(1234 mod 256) in
  hm |> IntMap.cardinal |> check int __LOC__ 1;
  (match hm |> IntMap.find_opt 1234 with
   | Some s ->
     s |> IntSet.cardinal |> check int __LOC__ 3;
     s |> IntSet.find_opt 14 |> Option.value ~default:3 |> check int __LOC__ 14;
     s |> IntSet.find_opt 13 |> Option.value ~default:3 |> check int __LOC__ 13;
     s |> IntSet.find_opt 12 |> Option.value ~default:3 |> check int __LOC__ 12
   | _ -> fail __LOC__);
  ()

let tc_sortedset () =
  let _s = IntSet.(empty |> add 1 |> add 2 |> add 2) in
  _s |> IntSet.cardinal |> check int __LOC__ 2;
  ()

let () =
  run
    "Mcdb2" [
    __FILE__ , [
      "set_up"      , `Quick, set_up;
      "tc_strstrmap", `Quick, tc_strstrmap;
      "tc_read4b",    `Quick, tc_read4b;
      "tc_blit",      `Quick, tc_blit;
      "tc_record",    `Quick, tc_record;
      "tc_fold_left", `Quick, tc_fold_left;
      "tc_fold_left_key", `Quick, tc_fold_left_key;
      "tc_find_first_key", `Quick, tc_find_first_key;
      "tc_array", `Quick, tc_array;
      "tc_scan_data", `Quick, tc_scan_data;
      "tc_sortedset", `Quick, tc_sortedset;
    ]
  ]
